import fetch from "isomorphic-fetch";
import * as qs from "query-string";

const DEFAULT_CONTENT_TYPE = 'application/json';

export default class  RequestManager {
    static get(url, param) {
        return new RequestManager('GET', url, param);
    }

    static post(url, param) {
        return new RequestManager('POST', url, param);
    }

    static head(url, param) {
        return new RequestManager('HEAD', url, param);
    }

    static options(url, param) {
        return new RequestManager('OPTIONS', url, param);
    }

    static delete(url, param) {
        return new RequestManager('DELETE', url, param);
    }

    static patch(url, param) {
        return new RequestManager('PATCH', url, param);
    }

    static put(url, param) {
        return new RequestManager('PUT', url, param);
    }

    constructor(method, url, param) {
        this.requestUrl = url;
        this.param = param;
        this.method = method;
        this.contentType = DEFAULT_CONTENT_TYPE;
        this.accept = DEFAULT_CONTENT_TYPE;
        this.headers = null;
        this.bd = null;
    }

    contentType(contentType) {
        this.contentType = contentType;
        return this;
    }

    accept(acceptType) {
        this.accept = acceptType;
        return this;
    }

    queryString(query) {
        this.qs = query;
        return this;
    }

    addHeaders(headers) {
        this.headers = {...this.headers, ...headers};
        return this;
    }

    body(body, contentType = DEFAULT_CONTENT_TYPE) {
        this.bd = body;
        this.contentType = contentType;
        return this;
    }

    execute() {
        const requestHeader = new Headers(this.headers || {});

        requestHeader.set('Content-Type', this.contentType);
        requestHeader.set('Accept', this.accept);

        let url = buildUrl(this.requestUrl, this.param);

        if (this.qs) {
            if (!url.endsWith('?')) {
                url += '?';
            }

            url += qs.stringify(this.qs);
        }

        const request = new Request(url, {
            method: this.method,
            credentials: 'include',
            headers: requestHeader,
            body: this.bd
        });

        const _self = this;

        console.debug('>>> Send Request: ' + this.method + ' ' + url);

        return new Promise((resolve, reject) =>
            fetch(request)
                .then(response => {
                    console.debug('<<< Response Status: ' + response.status + ' ' + response.statusText + ' from ' + _self.method + ' ' + url);

                    if (response.ok) {
                        return resolve(getCorrectResponse(response));
                    }

                    return reject(getCorrectResponse(response));
                })
        );
    }
}

function buildUrl(url, param) {
    if (url && param instanceof Object) {
        let newUrl = url;

        Object.keys(param).forEach(x => {
            newUrl = newUrl.replace(new RegExp(':\\b' + x + '\\b', 'g'), param[x]);
        });

        if (/:\b\w+\b/g.test(newUrl)) {
            const error = new Error('URL ' + newUrl + ' has unresolved parameters. Use RequestManager.verb(url, param) to initialize.');
            console.error(error);
        }

        return newUrl;
    }

    return url;
}

function getCorrectResponse(res)  {
    if (!res || !(res instanceof Response)) {
        console.debug('Not Expecting a Response');
        return res;
    }

    const contentType = res.headers.get('content-type');

    const contentTypeRaw = contentType?contentType.split(';').shift():undefined;

    if (contentTypeRaw === "application/json") {
        return res.json();
    }

    if (contentTypeRaw === "text/plain" || contentTypeRaw === 'text/html') {
        return res.text();
    }

    return res.blob();
}